package com.aramco.fion.util
{
	//import model.DataModel;
	
	import mx.collections.ArrayCollection;
	import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
	import mx.controls.advancedDataGridClasses.AdvancedDataGridColumnGroup;
	import mx.utils.ObjectUtil;
	
	
	
	public class XSLTParser
	{
		private static const PREFIX:String = 'XSLT_';
		private static const SCALE:Number = 8;
		public function XSLTParser()
		{
			//TODO: implement function
		}
		
		public static function parse(queryResult:XML, parsedResult:ArrayCollection, groupedColumns:Array, lookup:ArrayCollection, isLazyLoading:Boolean=false):void
		{
			if(!isLazyLoading)	//if not lazy loader, generate new groupedColumns, otherwise it's already filled.
			{
				generatHeader(queryResult.header.column, new ArrayCollection(groupedColumns));
				//generateTypeLookup(queryResult.header.column, lookup);
			}
			generateTypeLookup(queryResult.header.column, lookup);
			//parse data
			var dateColumns:Array = new Array();
			convertToArrayCollection(queryResult.data.row, parsedResult, dateColumns, isLazyLoading, lookup);
			//resize ADG header according to columns text width and data max width.
			Utilities.updateColumnsWidth(parsedResult, groupedColumns);
			//assigne date sortCompareFunction, if needed
			assignDateSortCompareFunction(groupedColumns, dateColumns);
		}
		public static function parseHeaderOnly(queryResult:XML, groupedColumns:Array, lookup:ArrayCollection, isLazyLoading:Boolean=false):void
		{
			generatHeader(queryResult.header.column, new ArrayCollection(groupedColumns));
			generateTypeLookup(queryResult.header.column, lookup);
		}
		//generate ADG columns
		private static function generatHeader(nodeset:XMLList, value:ArrayCollection, prefix:String=''):void
		{
			var groupColumn:AdvancedDataGridColumnGroup;
			var column:AdvancedDataGridColumn;
			for each(var node:XML in nodeset)
			{
				if(node.column.length() > 0)	//grouped column
				{
					groupColumn = new AdvancedDataGridColumnGroup();
					groupColumn.headerText = node.caption[0].toString();
					value.addItem(groupColumn);
					groupColumn.children = new Array();
					//groupColumn.minWidth = groupColumn.headerText.length * SCALE;
					generatHeader(node.column, new ArrayCollection(groupColumn.children), prefix + node.techname.toString() + '__');
				}
				else// if((node.children().length()) > 1) // a columns only
				{
					column = new AdvancedDataGridColumn();
					column.dataField = prefix + node.techname[0].toString();
					column.headerText = node.caption[0].toString();
				 	column.headerText = cleanString(column.headerText);	//remove break lines \r\n
					//column.minWidth = column.headerText.length * SCALE;
					value.addItem(column);
				}
			}
		}
		private static function generateTypeLookup(nodeset:XMLList, value:ArrayCollection, type:String='', prefix:String=''):void
		{
			var groupColumn:AdvancedDataGridColumnGroup;
			var column:AdvancedDataGridColumn;
			for each(var node:XML in nodeset)
			{
				if(node.attribute('type') != '' )
					type = node.attribute('type');
				if(node.column.length() > 0)	//grouped column
				{
					/* if(type == '')
						type = node.attribute('type'); */
					generateTypeLookup(node.column, value, type, prefix + node.techname.toString() + '__');
				}
				else	// a columns only. fille info
				{
					var info:ReportHeaderInfo = new ReportHeaderInfo();
					info.dataField = prefix + node.techname[0].toString();
					info.text = node.caption[0].toString();
					info.text = cleanString(info.text);	//remove break lines using str.replace(/^\n*/,'');
					info.type = type;
					value.addItem(info);
				}
			}
		}
		private static function convertToArrayCollection(nodeset:XMLList, result:ArrayCollection, dateColumns:Array, isLazyLoading:Boolean=false, lookup:Object=null):void
		{
			var hasChildren:Boolean;
			var item:Object;
			var p:String;	//object property name
			var cha:Object = new Object();	//list of CHAs
			var nodeName:Array;	//buffer for each CHA name
			var o:ReportHeaderInfo	//for loop - for each
			if(isLazyLoading)	//will be exec for first record only.
			{
				//two cases: 1- has sub orgs from original org. 2- replaced original org with new one(BEx hide org with one sub-node)
				//fill lookup for same CHA values from lazy loader object itself, except 0ORGUNIT
				if(lookup is ArrayCollection)
				{
					for each(o in lookup)//add CHA only
					{
						if(o.type == ReportHeaderInfo.CHARACTERISTICS)
						{
							nodeName = o.dataField.split('__');
							/*if(nodeName[nodeName.length-1].indexOf(DataModel.orgiobjnm) == -1)//add to list, exclude 0ORGUNIT CHA
								cha[o.dataField] = '';*///cha.push(o.dataField);
						}
					}
					lookup = cha;	//assign new lookup as array of CHA
					//fill lookup values from lazy loading object
					for(p in lookup)
					{
						lookup[p] = result.getItemAt(0)[p];
					}
				}
				//compare with original data first record - for BEx config to hide sub-node with one node only
				var isEqual:Boolean = false;
				for each(var tmpnode:XML in nodeset)	//loop rows
				{
					item = new Object();
					for each(var tmpCell:XML in tmpnode.children())//loop cells
					{
						if(tmpCell.name() != 'row')
							attach(tmpCell, item, dateColumns);
					}
					if(compareObj(item, result.getItemAt(0)))	//compare objects - if found, set flag and break
					{
						isEqual = true;
						break;
					}
				}
				if(!isEqual)	//special case exist because of BEx config.
				{
					//special case - if !isEqual but no more sub row, remove children
					if(XMLList(nodeset.row).length() == 0)	//no sub-orgs.	
					{
						//check if only itself, set as Leaf node
						item = new Object();
						for each(tmpCell in nodeset.children())//loop cells
						{
							if(tmpCell.name() != 'row')
								attach(tmpCell, item, dateColumns);
						}
						if(compareObj(item, result.getItemAt(0)))	//if same, set as Leaf node - unit of same div name
							nodeset[0].@drill = 'L';	//Leaf node.
					}
				}
				else
				{
					if(XMLList(nodeset.row).length() > 0)	//if = 0, this is a special case in BEx - org itself with no sub-orgs
						nodeset = nodeset.row;
					else
						nodeset[0].@drill = 'L';
				}
				//set result target
				result = new ArrayCollection(result.getItemAt(0).children);
			}
			for each(var element:XML in nodeset)	//one row each loop
			{
				item = new Object();
				hasChildren = false;
				if(element.@drill == 'E' || element.@drill == 'C')	//flag for lazy loading
				{
					item['children'] = new Array();
				}
				for each(var node:XML in element.children())	//loop cells of one row
				{
					if(node.name() != 'row')
					{
						attach(node, item, dateColumns);
					}
					else
						hasChildren = true;
						
				}
				//add single row to result.
				if(lookup is ArrayCollection)	//normal report
					result.addItem(item);
				else	//validate if this record should be part of Lazyloader or not
				{
					var isIncluded:Boolean = true;
					for(p in lookup)
					{
						if(item.hasOwnProperty(p))
						{
							if(item[p] != lookup[p])	//not valid record
							{
								isIncluded = false;
								break;
							}
						}
					}
					if(isIncluded)
						result.addItem(item);
				}
				//recursive call for sub-row, if exist
				if(hasChildren)
				{
					//no need to loop for rows, in case we have multiple children rows under one row. 
					//using element.row = list of sub rows which will be looped in next call
					item['children'] = new Array();
					if(lookup is ArrayCollection)	//generate lookup of CHA - not including org
					{
						//create look up of characteristics - not including org
						for each(o in lookup)//add CHA only
						{
							if(o.type == ReportHeaderInfo.CHARACTERISTICS)
							{
								nodeName = o.dataField.split('__');
								/*if(nodeName[nodeName.length-1].indexOf(DataModel.orgiobjnm) == -1)//add to list, exclude 0ORGUNIT CHA
									cha[o.dataField] = '';*///cha.push(o.dataField);
							}
						}
						lookup = cha;	//assign new lookup as array of CHA
					}
					//fill lookup values from lazy loading object
					for(p in lookup)
					{
						lookup[p] = item[p];
					}
					convertToArrayCollection(element.row, new ArrayCollection(item.children), dateColumns, false, lookup);
				}
			}
		}
		
		//add cell(s) to row object
		private static function attach(node:XML, item:Object, dateColumns:Array, prefix:String=''):void
		{
			if(node.@hasChildren == true)	//with childrens
			{
				for each(var subnode:XML in node.children())
				{
					attach(subnode, item, dateColumns, prefix + node.name().toString().replace(PREFIX, '') + '__')
				}
			}
			else	//no childrens
			{
				item[prefix + node.name().toString().replace(PREFIX, '')] = node[0].toString();
				//TODO: use a better way to get fields type. this is a temp solution
				//validate if value is a date, add node.name() to array if not added already.
				var splitted:Array = node[0].toString().split('/');
				if(splitted.length == 3)
				{
					//validate that all are number
					if(splitted[0].length <= 2 && splitted[1].length <= 2 && splitted[2].length == 4)
					{
						var propertyName:String = prefix + node.name().toString().replace(PREFIX, '');
						if(dateColumns.indexOf(propertyName) == -1)//add to array if not added already
							dateColumns.push(propertyName);
					}
				} 
			}
		}
		
		//remove line breaks from a string
		private static function cleanString(value:String):String
		{
			//value.replace(/^[\r\n]+/, ' ');
			var result:String = '';
			for(var i:Number=0 ; i < value.length ; i++)
			{
				if(value.charAt(i) != '\r' && value.charAt(i) != '\n')
					result += value.charAt(i);
				else if(value.charAt(i) == '\n')
					result += ' '; 
			}
			return result;
		}
		
		//assign sort compare function for dates
		private static function assignDateSortCompareFunction(groupedColumns:Array, dateColumns:Array):void
		{
			for each(var o:Object in groupedColumns)
			{
				if(o is AdvancedDataGridColumnGroup)
					assignDateSortCompareFunction(AdvancedDataGridColumnGroup(o).children, dateColumns);
				else if(o is AdvancedDataGridColumn)
				{
					if(dateColumns.indexOf(AdvancedDataGridColumn(o).dataField) != -1)//is date column
					{
						dateSortFunc(o as AdvancedDataGridColumn, o.dataField);
					}
				}
			}
		}
		private static function dateSortFunc(column:AdvancedDataGridColumn, dataField:String):void
		{
			column.sortCompareFunction = function (objA:Object, objB:Object):int
							{
								var dateA:Date = new Date(objA[dataField]);
								var dateB:Date = new Date(objB[dataField]);
								return ObjectUtil.compare(dateA, dateB);
							}
		}
		
		//sortCompareFunction for date columns
		/* private function dateSortCompareFunction(itemA:Object, itemB:Object):int
		{
			var dateA:Date = new Date(Date.parse(itemA.dob));
			var dateB:Date = new Date(Date.parse(itemB.dob));
			return ObjectUtil.dateCompare(dateA, dateB);
		} */
		
		//compare two objects values. Obj1 is the original
		private static function compareObj(obj1:Object, obj2:Object):Boolean
		{
			for(var p:String in obj1)
			{
				if(obj1[p] != obj2[p])
					return false;
			}
			return true;
		}

	}
}